/**
 * Copyright(c) 2014 Xunton Solutions
 *
 * History:
 *   15-1-12 下午6:48 Created by dwzhang
 */
package com.jade.bss.api.controller;

import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jade.framework.base.util.DataUtils;
import com.jade.framework.base.vfile.VFileConstants;
import com.jade.framework.base.vfile.VFileInfo;
import com.jade.framework.base.vfile.VFileManager;
import com.jade.framework.base.vfile.VFileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 文件
 *
 * @author <a href="mailto:dwzhang@xunton.com">dwzhang</a> 
 * @version 1.0 15-1-12 下午6:48
 */
@RestController
@RequestMapping ("/vfs")
public class VFileController
{
    private Log log = LogFactory.getLog(VFileController.class);

    private static boolean matches(String matchHeader, String toMatch)
    {
        String[] matchValues = matchHeader.split("\\s*,\\s*");
        Arrays.sort(matchValues);
        return Arrays.binarySearch(matchValues, toMatch) > -1
                || Arrays.binarySearch(matchValues, "*") > -1;
    }

    @RequestMapping (value = "/{dataType}/**")
    public void downloadFile(@PathVariable (value = "dataType") String dataType,
                             HttpServletRequest request, HttpServletResponse response)
            throws Exception
    {
        try {
            VFileManager fileManager = VFileUtils.getFileManager(dataType);
            String requestUri = request.getRequestURI();
            int index = requestUri.indexOf(dataType);
            String fileId = requestUri.substring(dataType.length() + index);
            VFileInfo info = fileManager.getFileInfo(dataType, fileId);
            if (info != null) {
                response.setContentType(info.getContentType());
                if (StringUtils.equalsIgnoreCase(VFileConstants.DATA_TYPE_VIDEO, dataType)) {
                    downloadVideo(fileManager, info, request, response);
                }
                else {
                    response.setHeader("Content-Length", String.valueOf(info.getSize()));
                    fileManager.downloadFile(info, response.getOutputStream());
                }
            }
            else {
                //return 404.
                response.setStatus(404);
            }
        }
        catch (Throwable t) {
            log.error("Can't download file:", t);
            response.sendError(500);
        }
        finally {
            IOUtils.closeQuietly(response.getOutputStream());
        }
    }

    private static final int DEFAULT_BUFFER_SIZE = 10240;

    protected void downloadVideo(VFileManager fileManager, VFileInfo info, HttpServletRequest request,
                                 HttpServletResponse response)
            throws IOException
    {
        long fileLength = info.getSize();
        // Prepare some variables. The ETag is an unique identifier of the file.
        String fileName = info.getFileName();
        long lastModified = info.getLastModified();
        String eTag = fileName + "_" + fileLength + "_" + lastModified;

        // Validate request headers for caching ---------------------------------------------------

        // If-None-Match header should contain "*" or ETag. If so, then return 304.
        String ifNoneMatch = request.getHeader("If-None-Match");
        if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
            response.setHeader("ETag", eTag); // Required in 304.
            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        // If-Modified-Since header should be greater than LastModified. If so, then return 304.
        // This header is ignored if any If-None-Match header is specified.
//        long ifModifiedSince = request.getDateHeader("If-Modified-Since");
//        if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
//            response.setHeader("ETag", eTag); // Required in 304.
//            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
//            return;
//        }

        // Validate request headers for resume ----------------------------------------------------

        // If-Match header should contain "*" or ETag. If not, then return 412.
        String ifMatch = request.getHeader("If-Match");
        if (ifMatch != null && !matches(ifMatch, eTag)) {
            response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
            return;
        }

        // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
        long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
        if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
            response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
            return;
        }

        // tell the client to allow accept-ranges
        response.reset();
        response.setBufferSize(DEFAULT_BUFFER_SIZE);
        response.setHeader("Accept-Ranges", "bytes");
        response.setContentType("video/mp4");
        response.setHeader("ETag", eTag);
        response.setDateHeader("Last-Modified", lastModified);
        // client requests a file block download start byte
        long from = 0, to = 0;
        String range = request.getHeader("Range");
        if (range != null) {
            if (StringUtils.startsWith(range, "bytes=")) {
             range = range.substring(6);
            }
            String[] array = range.split("-");
            from = DataUtils.getLong(array[0], 0);
            if (array.length > 1) {
                to = DataUtils.getLong(array[1], 0);
            }
        }
        to = to == 0 ? fileLength - 1 : to;
        long size = to - from + 1;
        response.setHeader("Content-Length", String.valueOf(size));
        if (to >= (from + DEFAULT_BUFFER_SIZE - 1) || range != null) {
            // 断点开始
            // 响应的格式是:
            // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
            response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);
            String contentRange = new StringBuilder("bytes ")
                    .append(from).append("-").append(to).append("/").append(fileLength).toString();
            response.setHeader("Content-Range", contentRange);
        }
        //String fileName = info.getFileName();
        //response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
        fileManager.downloadFile(info, response.getOutputStream(), from, size);
    }
}
